#include "headers.h"

ParserGenerator::ParserGenerator(const Grammar* grammar)
{
    this->grammar=grammar;
    generateSets();
    GenerateParserTables();
}

void ParserGenerator::generateSets()
{    
    sets.clear();
    Set::SetCount=0;
    Set* mainSet=new Set(NULL, "START");
    mainSet->addRWI(new Rule("START", QStringList(grammar->firstSymbol)), 0);
    QStringList done;
    done.append(grammar->firstSymbol);
    for(int i=0; i<grammar->rules.length(); i++)
    {
        const Rule* rule=&grammar->rules[i];
        if(rule->LHS==grammar->firstSymbol)
        {
            mainSet->addRWI(rule, 0);
            if(rule->RHS.isEmpty())
                continue;
            if(!done.contains(rule->RHS.first()))
            {
                recursive(mainSet, rule->RHS.first());
                done.append(rule->RHS[0]);
            }
        }
    }
    this->sets.append(*mainSet);
    generateSubSet(mainSet);
}

void ParserGenerator::generateSubSet(const Set* genSetParam)
{
    QStringList done;
    for(int i=0; i<genSetParam->rules.length(); i++)
    {
        if(genSetParam->rules[i].finished())
            continue;
        QString currSymbol=genSetParam->rules[i].getCurRHS();
        if(done.contains(currSymbol))
            continue;
        Set* subSet=new Set(genSetParam, currSymbol);
        for(int j=0; j<genSetParam->rules.length(); j++)
        {
            const RWI* looprwi=&genSetParam->rules[j];
                if(currSymbol==looprwi->getCurRHS())
                {
                    subSet->addRWI(looprwi->rule, looprwi->index+1);
                    recursive(subSet, looprwi->getNextRHS());
                }
        }
        done.append(currSymbol);
        if(subSet->isEmpty())
            continue;
        if(sets.contains(*subSet))
        {
            int a=sets.indexOf(*subSet);
            sets[a].genSet.insert(genSetParam);
            continue;
        }
        sets.append(*subSet);
        generateSubSet(subSet);
    }
}

void ParserGenerator::recursive(Set* set, QString lhs)
{
    for(int i=0; i<grammar->rules.length(); i++)
    {
        const Rule* rule=&grammar->rules[i];
        if(rule->LHS==lhs)
        {
            set->addRWI(rule, 0);
            if(rule->RHS.length()>0)
                if(rule->RHS[0]!=lhs)
                    recursive(set, rule->RHS[0]);
        }
    }
}

ParserGenerator::~ParserGenerator()
{
    delete actionTable;
    delete gotoTable;
}

const ControlTable* ParserGenerator::GetActionTable()
{
    return actionTable;
}

const ControlTable* ParserGenerator::GetGotoTable()
{
    return gotoTable;
}

QStringList ParserGenerator::GetTerminalsAlphabet()
{
    QStringList result;
    foreach(QString str, grammar->terminals)
        result.append(str);
    result.append("");
    return result;
}

QStringList ParserGenerator::GetNonterminalsAlphabet()
{
    return grammar->nonterminals.toList();
}

QList<int> ParserGenerator::GetStates()
{
    QList<int> result;
    foreach(Set set, sets)
    {
        result.append(set.number);
    }
    return result;
}

void ParserGenerator::GenerateParserTables()
{
    actionTable=new ControlTable(GetStates(), GetTerminalsAlphabet());
    gotoTable=new ControlTable(GetStates(), GetNonterminalsAlphabet());

    int errorCount=0;
    foreach(Set set, sets)
    {
        if(grammar->terminals.contains(set.genSymbol))
        {
            foreach(const Set* genSet, set.genSet)
            {
                {
                    Cell* tmpCell=new Cell();
                    actionTable->GetValue(genSet->number, set.genSymbol, tmpCell);
                    if(tmpCell->action==SHIFT)
                        errors.append(QObject::tr("%1. <Action Table> Shift-Shift conflict found in state=\'%2\' symbol=\'%3\'").arg(++errorCount).arg(genSet->number).arg(set.genSymbol));
                    if(tmpCell->action==REDUSE)
                        errors.append(QObject::tr("%1. <Action Table> Reduse-Shift conflict found in state=\'%2\' symbol=\'%3\'").arg(++errorCount).arg(genSet->number).arg(set.genSymbol));
                }
                actionTable->SetValue(genSet->number, set.genSymbol, Cell(SHIFT, set.number));
            }
        }
        if(grammar->nonterminals.contains(set.genSymbol))
        {
            foreach(const Set* genSet, set.genSet)
            {
                Cell* tmpCell=new Cell();
                if(!gotoTable->GetValue(genSet->number, set.genSymbol, tmpCell))
                {
                    if(tmpCell->action!=ERROR)
                        errors.append(QObject::tr("%1. <Goto Table> Conflict found in state=\'%2\' symbol=\'%3\'").arg(++errorCount).arg(genSet->number).arg(set.genSymbol));
                }
               gotoTable->SetValue(genSet->number, set.genSymbol, Cell(GOTO, set.number));
            }
        }
        foreach(RWI rwi, set.rules)
        {
            if(rwi.finished())
            {
                if(rwi.rule->LHS == "START")
                    actionTable->SetValue(set.number, "", Cell(ACCEPT, 0));
                else
                    foreach(QString str, actionTable->keys2())
                    {
                        Cell* tmpCell=new Cell();
                        actionTable->GetValue(set.number, str, tmpCell);
                        if(tmpCell->action==SHIFT)
                            errors.append(QObject::tr("%1. <Action Table> Shift-Reduce conflict found in state=\'%2\' symbol=\'%3\'").arg(++errorCount).arg(set.number).arg(str));
                        else if(tmpCell->action==REDUSE)
                            errors.append(QObject::tr("%1. <Action Table> Reduce-Reduce conflict found in state=\'%2\' symbol=\'%3\'").arg(++errorCount).arg(set.number).arg(str));
                        else
                            actionTable->SetValue(set.number, str, Cell(REDUSE, grammar->rules.indexOf(*rwi.rule)));
                    }
            }
        }
    }
}

void ParserGenerator::SaveControlTables(QString fileName)
{
    QFile file(fileName);
    if(!file.open(QIODevice::WriteOnly|QIODevice::Text))
    {
        QMessageBox::information(NULL, QObject::tr("Parser generator"),
                                 QObject::tr("Error while opening file: %1")
                                 .arg(file.errorString()));
        return;
    }
    QTextStream stream(&file);

    QDomDocument domDoc;
    domDoc.appendChild(domDoc.createComment("This file is ganerated automaticaly. Do not modify, unless you exactly know what to do!"));
    QDomElement root = domDoc.createElement("parser_tables");

    QDomElement _grammar = domDoc.createElement("grammar");
    QDomElement nonterminals = domDoc.createElement("nonterminals");
    QDomElement terminals = domDoc.createElement("terminals");
    QDomElement rules = domDoc.createElement("rules");
    QDomElement firstsymbol = domDoc.createElement("first_symbol");

    QDomElement _tables = domDoc.createElement("tables");
    QDomElement states = domDoc.createElement("states");
    QDomElement actiontbl = domDoc.createElement("action_table");
    QDomElement gototbl = domDoc.createElement("goto_table");

    QString tmp=" ";
    foreach(QString str, grammar->nonterminals)
    {
        tmp.append(str.append(" "));
    }
    QDomText text= domDoc.createTextNode(tmp);
    nonterminals.appendChild(text);

    tmp=" ";
    foreach(QString str, grammar->terminals)
    {
        tmp.append(str.append(" "));
    }
    text= domDoc.createTextNode(tmp);
    terminals.appendChild(text);

    tmp="\n\t";
    foreach(Rule rule, grammar->rules)
    {
        tmp.append(rule.toString().append("\n\t"));
    }
    text= domDoc.createTextNode(tmp);
    rules.appendChild(text);

    tmp=" ";
    foreach(Set set, sets)
    {
        tmp.append(QString::number(set.number).append(" "));
    }
    text= domDoc.createTextNode(tmp);
    states.appendChild(text);

    firstsymbol.appendChild(domDoc.createTextNode(grammar->firstSymbol));


    foreach(int state, actionTable->keys1())
    {
        foreach(QString symbol, actionTable->keys2())
        {
            Cell* bufer=new Cell();
            if(actionTable->GetValue(state, symbol, bufer))
            {
                QMessageBox::information(NULL, QObject::tr("Parser generator"),
                                         QObject::tr("Error while writing file. Wrong value in ActionTable [%1][%2]")
                                         .arg(QString::number(state)).arg(symbol));
                return;
            }
            if(bufer->action==ERROR)
                continue;
            QDomElement cell=domDoc.createElement("cell");
            cell.setAttribute("state", state);
            cell.setAttribute("symbol", symbol);
            cell.setAttribute("action", bufer->action);
            cell.setAttribute("value", bufer->value);
            actiontbl.appendChild(cell);
            delete bufer;
        }
    }

    foreach(int state, gotoTable->keys1())
    {
        foreach(QString symbol, gotoTable->keys2())
        {
            Cell* bufer=new Cell();
            if(gotoTable->GetValue(state, symbol, bufer))
            {
                QMessageBox::information(NULL, QObject::tr("Parser generator"),
                                         QObject::tr("Error while writing file. Wrong value in GotoTable [%1][%2]")
                                         .arg(QString::number(state)).arg(symbol));
                return;
            }
            if(bufer->action==ERROR)
                continue;
            QDomElement cell=domDoc.createElement("Cell");
            cell.setAttribute("state", state);
            cell.setAttribute("symbol", symbol);
            cell.setAttribute("action", bufer->action);
            cell.setAttribute("value", bufer->value);
            gototbl.appendChild(cell);
            delete bufer;
        }
    }

    _grammar.appendChild(nonterminals);
    _grammar.appendChild(terminals);
    _grammar.appendChild(rules);
    _grammar.appendChild(firstsymbol);

    _tables.appendChild(states);
    _tables.appendChild(actiontbl);
    _tables.appendChild(gototbl);

    root.appendChild(_grammar);
    root.appendChild(_tables);

    domDoc.appendChild(root);

    domDoc.save(stream, 2);
}

const QStringList* ParserGenerator::GetErrorsList()
{
    return &errors;
}
